/**
 * @Title: GlobalExceptionHandler.java
 * @Package com.smartcity.scity.common.core.handler
 * @Description: 统一异常处理
 * @author liaoc
 * @date 2020年2月22日
 */
package com.ujcms.commons.exception;

import com.ujcms.commons.web.Responses;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.impl.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;

import java.util.List;
import java.util.Objects;
import java.util.Set;


/**
 * 全局异常捕获处理
 *
 * @author byb
 * @date 2020/02/22
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {


    @Value("${spring.servlet.multipart.max-file-size}")
    private String maxFileSize;

    @Value("${spring.servlet.multipart.max-request-size}")
    private String maxTotalFileSize;

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = MultipartException.class)
    @ResponseBody
    public ResponseEntity<Responses.Body> handleBusinessException(MaxUploadSizeExceededException ex) {
        String msg;
        if (ex.getCause().getCause() instanceof FileSizeLimitExceededException) {
            log.error("上传文件过大:{}", ex.getMessage());
            msg = "上传文件过大[单文件大小不得超过" + maxFileSize + "]";
        } else if (ex.getCause().getCause() instanceof SizeLimitExceededException) {
            log.error("上传总文件过大:{}", ex.getMessage());
            msg = "上传文件过大[总上传文件大小不得超过" + maxTotalFileSize + "]";
        } else {
            msg = "上传文件失败";
        }
        return Responses.failure(msg);
    }


    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({HttpMessageNotReadableException.class, MissingServletRequestParameterException.class,
            BindException.class, ServletRequestBindingException.class, MethodArgumentNotValidException.class, ConstraintViolationException.class})
    public ResponseEntity<Responses.Body> handleHttpMessageNotReadableException(Exception e) {
        log.error("参数解析失败", e);
        BindingResult result;
        //处理@valid校验异常
        if (e instanceof BindException) {
            result = ((BindException) e).getBindingResult();
            // 1、先判断数据是否合法
            if (result.hasErrors()) {
                List<FieldError> list = result.getFieldErrors();
                for (FieldError error : list) {
                    if (error.isBindingFailure()) {
                        return Responses.failure("请检查参数字段" + error.getField() + "的数据类型");
                    } else {
                        return Responses.failure(Objects.requireNonNull(error.getDefaultMessage()));
                    }
                }
            }
        }

        /*处理@RequestParam的校验异常*/
        if (e instanceof MissingServletRequestParameterException) {
            return Responses.failure("缺少必要参数:" + ((MissingServletRequestParameterException) e).getParameterName());
        }

        /*处理@RequestBody @valid的校验异常*/
        if (e instanceof MethodArgumentNotValidException) {
            result = ((MethodArgumentNotValidException) e).getBindingResult();
            // 1、先判断数据是否合法
            if (result.hasErrors()) {
                StringBuilder errorMessage = new StringBuilder();
                List<ObjectError> list = result.getAllErrors();
                for (ObjectError error : list) {
                    errorMessage.append(error.getDefaultMessage()).append(" ");
                }
                return Responses.failure(errorMessage.toString());
            }
        }
        /*处理@Validated+@valid的校验异常*/
        if (e instanceof ConstraintViolationException) {
            Set<ConstraintViolation<?>> res = ((ConstraintViolationException) e).getConstraintViolations();

            /*如果res为空时, 直接返回message*/
            if (Objects.isNull(res)) {
                return Responses.failure(e.getMessage());
            }
            // 1、先判断数据是否合法
            if (!res.isEmpty()) {
                StringBuilder errorMessage = new StringBuilder();
                for (ConstraintViolation<?> error : res) {
                    errorMessage.append(error.getMessageTemplate()).append(" ");
                }
                return Responses.failure(errorMessage.toString());
            }
        }
        return Responses.failure("参数异常");
    }


    /**
     * 405 - Method Not Allowed 带有@ResponseStatus注解的异常类会被ResponseStatusExceptionResolver 解析
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<Responses.Body> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        log.error("不支持当前请求方法", e);
        return Responses.failure("不支持当前请求方法");
    }

    /**
     * 其他全局异常在此捕获
     *
     * @param e exception
     * @return HttpResult
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Throwable.class)
    public ResponseEntity<Responses.Body> handleException(Throwable e) {
        log.error("服务运行异常", e);
        if (e instanceof org.apache.ibatis.exceptions.PersistenceException) {
            return Responses.failure("数据库异常");
        } else if (e instanceof BusinessException) {

            return Responses.failure(e.getMessage());
        } else if (e.getCause() instanceof BusinessException) {
            return Responses.failure(e.getMessage());
        } else {
            return Responses.failure("网络异常");
        }
    }
}
